using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;
using System.Threading;
using System.Runtime.InteropServices;
using System.Reflection;
using Microsoft.Office.Project.EPMSync.Config.Library;
using System.Globalization;

namespace Microsoft.Office.Project.EPMSync.Service
{
    /// <summary>
    /// Base class for the EPMSync Service.  This handles the calls from the
    /// service control manager and spawns and manages the worker thread.
    /// </summary>
    public partial class SyncService : ServiceBase
    {
        /// <summary>
        /// Allows you to manipulate the status of the running service (Starting, Strated, etc)
        /// </summary>
        /// <param name="hServiceStatus">Handle to the service.</param>
        /// <param name="lpServiceStatus">Status enumeration to set the status.</param>
        /// <returns></returns>
        [DllImport("ADVAPI32.DLL", EntryPoint = "SetServiceStatus")]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool SetServiceStatus( IntPtr hServiceStatus, ServiceStatus lpServiceStatus);

        /// <summary>
        /// Private variable for tracking the state of the service.
        /// </summary>
        private ServiceStatus _serviceStatus;

        /// <summary>
        /// Flag for recognizing if there has been a request to start Sync immediately.
        /// </summary>
        private bool _executeSyncNow = false;

        /// <summary>
        /// Thread which runs the actual synchronization
        /// </summary>
        private Thread _workerThread = null;

        /// <summary>
        /// Reference to the tracing component used throughout the application
        /// </summary>
        private Tracing _tracing = null;

        /// <summary>
        /// Event to signal the worker thread to exit.
        /// </summary>
        private ManualResetEvent _pause = new ManualResetEvent(false);

        /// <summary>
        /// Constuctor for the service.
        /// </summary>
        public SyncService()
        {
            InitializeComponent();
        }

        #region Public Methods

        /// <summary>
        /// Method for the worker thread to query if the SyncNow custom command has been invoked.
        /// </summary>
        /// <returns>True if SyncNow has been triggered.</returns>
        public bool GetSyncNowCallback()
        {
            return _executeSyncNow;
        }

        /// <summary>
        /// Method for the worker thread set the status of the SyncNow flag.  Usually called to clear
        /// the thread after reacting to it being set.
        /// </summary>
        /// <param name="syncNow">Boolean value to set SyncNow with.</param>
        public void SetSyncNowCallback(bool syncNow)
        {
            _executeSyncNow = syncNow;
        }

        /// <summary>
        /// Property to return the active tracing service
        /// </summary>
        public Tracing TracePtr
        {
            get
            {
                return _tracing;
            }
        }

        #endregion

        #region Overriden Service Methods

        /// <summary>
        /// Receives the Start command from the Service Control Manager.  Start the tracing 
        /// and spawns the worker thread.
        /// </summary>
        /// <param name="args">Optional. Set the tracing level 1-4.</param>
        protected override void OnStart(string[] args)
        {
            if (args.Length == 1)
                _tracing = new Tracing((TraceLevel)Convert.ToInt32(args[0], CultureInfo.InvariantCulture));
            else
                _tracing = new Tracing(TraceLevel.Info);

            IntPtr handle = this.ServiceHandle;
            _serviceStatus.currentState = (int)ServiceState.StartPending;
            SetServiceStatus(handle, _serviceStatus);

            // Start a separate thread that does the actual work.

            if ((_workerThread == null) ||
                ((_workerThread.ThreadState &
                 (System.Threading.ThreadState.Unstarted | System.Threading.ThreadState.Stopped)) != 0))
            {
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, "Starting the service worker thread");
                SyncThread st = new SyncThread(_tracing, _pause, GetSyncNowCallback, SetSyncNowCallback);

                _workerThread = new Thread(new ThreadStart(st.WorkerMethod));
                _workerThread.Start();
            }
            if (_workerThread != null)
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "Worker thread state = {0}", _workerThread.ThreadState.ToString()));

            _serviceStatus.currentState = (int)ServiceState.Running;
            SetServiceStatus(handle, _serviceStatus);
            _pause.Set();
        }

        /// <summary>
        /// Receives the Stop command from the Service Control Manager.  Signals the worker thread to exit.
        /// Waits for the thread to exit.
        /// </summary>
        protected override void OnStop()
        {
            this.RequestAdditionalTime(4000);
            // Signal the worker thread to exit.
            if ((_workerThread != null) && (_workerThread.IsAlive))
            {
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, "Stopping the service worker thread");
                _pause.Reset();
                Thread.Sleep(5000);
                _workerThread.Abort();
            }
            if (_workerThread != null)
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "OnStop Worker thread state = {0}", _workerThread.ThreadState.ToString()));

            // Indicate a successful exit.
            this.ExitCode = 0;
        }

        /// <summary>
        /// Sends a request to the worker thread to pause.
        /// </summary>
        protected override void OnPause()
        {
            // Pause the worker thread.
            if ((_workerThread != null) &&
                (_workerThread.IsAlive) &&
                ((_workerThread.ThreadState &
                 (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) == 0))
            {
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, "Pausing the service worker thread");
                _pause.Reset();
                Thread.Sleep(5000);
            }

            if (_workerThread != null)
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "OnPause Worker thread state = {0}", _workerThread.ThreadState.ToString()));
        }

        /// <summary>
        /// Sends a request to the worker thread to pause.
        /// </summary>
        protected override void OnContinue()
        {

            // Signal the worker thread to resume.
            if ((_workerThread != null) &&
                ((_workerThread.ThreadState &
                 (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) != 0))
            {
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, "Resuming the service worker thread");
                _pause.Set();
            }
            if (_workerThread != null)
                _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "OnContinue Worker thread state = {0}", _workerThread.ThreadState.ToString()));
        }

        /// <summary>
        /// Handle the custom command coming to the EPMSync service.
        /// </summary>
        /// <param name="command">CommandID for the desired command.</param>
        protected override void OnCustomCommand(int command)
        {
            _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "Custom command received = {0}", command.ToString(CultureInfo.InvariantCulture)));

            switch (command)
            {
                case (int)SimpleServiceCustomCommand.ExecuteSyncNow:
                    _executeSyncNow = true;
                    break;

                case (int)SimpleServiceCustomCommand.CheckWorker:
                    _tracing.WriteTrace(_tracing.MainSwitch.TraceInfo, String.Format(CultureInfo.InvariantCulture, "OnCustomCommand Worker thread state = {0}", _workerThread.ThreadState.ToString()));
                    break;

                default:
                    break;
            }
        }

        #endregion
    }
}
